/* Copyright (c) 2004-2008, Andriy Nikolov
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.processmining.email;

import java.util.*;

/**
 * <p>Represents the data for the message frequency chart.
 *
 * @author Andriy Nikolov
 * @version 1.0
 */
public class FrequencyChart {
  Date startDate, endDate;
  int intervalLength;
  int intervalMeasurement;
  EMailLog eMailLog;
  GregorianCalendar calendar;
  Vector borderDates;

  LinkedList orderedMessages = null;

  int[] values;
  Date[] intervalBorders;

  public FrequencyChart() {
    intervalLength = 1;
    intervalMeasurement = Calendar.DAY_OF_MONTH;
    calendar = new GregorianCalendar();
    borderDates = new Vector();
  }

  /**
   * Creates a MessageFrequencyChart instance based on a particular EMailLog.
   *
   * @param log EMailLog
   */
  public FrequencyChart(EMailLog log) {
    intervalLength = 1;
    intervalMeasurement = Calendar.DAY_OF_MONTH;
    calendar = new GregorianCalendar();
    borderDates = new Vector();
    eMailLog = log;
  }

  /**
   * Sets the e-mail log to take the source data from.
   *
   * @param log EMailLog
   */
  public void setEMailLog(EMailLog log) {
    eMailLog = log;
  }

  public EMailLog getEMailLog() {
    return eMailLog;
  }

  /**
   * Returns the earliest year, in which the message from the e-mail log were
   * sent.
   *
   * @return int
   */
  public int getMinimumYear() {
    EMailMessage message;
    try {
      if (orderedMessages==null) {
        orderedMessages = eMailLog.getOrderedMessages();
      }
      message = (EMailMessage)orderedMessages.getFirst();
      calendar.setTime(message.getSentOn());
      return calendar.get(Calendar.YEAR);
    } catch (Exception e) {
      return Calendar.getInstance().get(Calendar.YEAR);
    }
  }

  /**
   * Returns the latest year, in which the message from the e-mail log were sent.
   *
   * @return int
   */
  public int getMaximumYear() {
    EMailMessage message;
    try {
      if (orderedMessages==null) {
        orderedMessages = eMailLog.getOrderedMessages();
      }
      message = (EMailMessage)orderedMessages.getLast();
      calendar.setTime(message.getSentOn());
      return calendar.get(Calendar.YEAR);
    } catch (Exception e) {
      return Calendar.getInstance().get(Calendar.YEAR);
    }
  }

  /**
   * Sets the start date for the chart (the leftmost point on the X-axis).
   *
   * @param date Date
   */
  public void setStartDate(Date date) {
    startDate = date;
  }

  /**
   * Gets the start date for the chart.
   *
   * @return Date
   */
  public Date getStartDate() {
    return startDate;
  }

  /**
   * Sets the end date for the chart (the rightmost point on the X-axis).
   *
   * @param date Date
   */
  public void setEndDate(Date date) {
    endDate = date;
  }

  public Date getEndDate() {
    return endDate;
  }

  /**
   * Gets the length of the interval between two neighbor points on the X-axis
   * (in interval measurement units).
   *
   * @return int
   */
  public int getIntervalLength() {
    return intervalLength;
  }

  /**
   * Sets the length of the interval between two neighbor points on the X-axis
   * (in interval measurement units).
   *
   * @param length int
   */
  public void setIntervalLength(int length) {
    intervalLength = length;
  }

  /**
   * Gets the time interval measurement unit.
   *
   * @return int
   */
  public int getIntervalMeasurement() {
    return intervalMeasurement;
  }

  /**
   * Sets the interval measurement unit. Can be Calendar.WEEK_OF_YEAR,
   * Calendar.MONTH, Calendar.YEAR, Calendar.DAY_OF_MONTH.
   *
   * @param measurement int
   */
  public void setIntervalMeasurement(int measurement) {
    intervalMeasurement = measurement;
  }

  /**
   * Gets the list of dates, which are borders between intervals (start date and
   * end date included).
   *
   * @return Vector
   */
  public Vector getBorderDates() {
    return borderDates;
  }

  /**
   * Gets an array of values. Each value represents the number of messages sent
   * in a particular time interval.
   *
   * @return int[]
   */
  public int[] getValues() {
    return values;
  }

  /**
   * Gets the ith value. Each value represents the number of messages sent in a
   * particular time interval.
   *
   * @param i int
   * @return int
   */
  public int getValue(int i) {
    if ((i<values.length)&&(i>=0)) {
      return values[i];
    } else {
      return 0;
    }
  }

  private void adjustBorders() {
    int cur;
    calendar.setTime(startDate);

    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);

    switch(intervalMeasurement) {
      case Calendar.WEEK_OF_YEAR:
        cur = calendar.get(Calendar.DAY_OF_WEEK);
        calendar.add(Calendar.DAY_OF_WEEK, 1-cur);
        break;
      case Calendar.MONTH:
        cur = calendar.get(Calendar.DAY_OF_MONTH);
        calendar.add(Calendar.DAY_OF_MONTH, 1-cur);
        break;
      case Calendar.YEAR:
        cur = calendar.get(Calendar.DAY_OF_YEAR);
        calendar.add(Calendar.DAY_OF_YEAR, 1-cur);
        break;
    }
    startDate = calendar.getTime();
    calendar.setTime(endDate);
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    switch(intervalMeasurement) {
      case Calendar.WEEK_OF_YEAR:
        cur = calendar.get(Calendar.DAY_OF_WEEK);
        calendar.add(Calendar.DAY_OF_WEEK, 7-cur);
        break;
      case Calendar.MONTH:
        cur = calendar.get(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        break;
      case Calendar.YEAR:
        cur = calendar.get(Calendar.DAY_OF_YEAR);
        calendar.set(Calendar.DAY_OF_YEAR, calendar.getActualMaximum(Calendar.DAY_OF_YEAR));
        break;
    }
    endDate = calendar.getTime();
  }

  private void calculateBorders() {
    Date curStartDate;
    borderDates.clear();

    adjustBorders();
    calendar.setTime(startDate);
    curStartDate = startDate;
    while (curStartDate.before(endDate)) {
      borderDates.add(curStartDate);
      calendar.add(intervalMeasurement, intervalLength);
      curStartDate = calendar.getTime();
    }
    borderDates.add(endDate);
    // debug
 /*   for (int i=0;i<borderDates.size();i++) {
      curStartDate = (Date)borderDates.get(i);
      System.out.println(curStartDate.toString());
    }*/
  }

  private void calculateValues() {
    Enumeration enumMessages;
    EMailMessage iterMessage;
    Date curStartDate, curEndDate;
    int i;

    values = new int[borderDates.size()-1];
    for (i=0;i<borderDates.size()-1;i++) {
      values[i] = 0;
    }
    enumMessages = eMailLog.getMessageList().elements();
    while(enumMessages.hasMoreElements()) {
      iterMessage = (EMailMessage)enumMessages.nextElement();
      for(i=0;i<borderDates.size()-1; i++) {
        curStartDate = (Date)borderDates.get(i);
        curEndDate = (Date)borderDates.get(i+1);
        if((iterMessage.getSentOn().before(curEndDate))&&(!iterMessage.getSentOn().before(curStartDate))) {
          values[i]++;
          break;
        }
      }
    }
    // debug
/*    for (i=0;i<borderDates.size()-1;i++) {
      System.out.println(values[i]);
    }*/

  }

  /**
   * Recalculates the chart's borders and values.
   */
  public void refreshChart() {
    calculateBorders();
    calculateValues();
  }
}
